#include "SystemRecommendation.h"
#include <iostream>
#include <stdlib.h>
#include <fstream>

SystemRecommendation::SystemRecommendation(){

}

SystemRecommendation::~SystemRecommendation(){

}

double sumDamage(Algorithm* a){
	double d = 0;
	map<int, double>::iterator it;
	map<int, double>* damage = a->getDamage();
	for (it = damage->begin(); it != damage->end(); it++){
		d += it->second;
	}
	return d;
}

void SystemRecommendation::First_Next(SystemView* sys, Writer* writer, Parser* parser){
	NextAlgo* next = new NextAlgo(sys, 0, writer, parser, 0.5, 1000);
	next->runAlgo(1);
	writer->DeviceUtilStatistics("1");
	writer->writeGroupRT("1");
	delete next;
}

double SystemRecommendation::RunNext(SystemView* sys, Writer* writer, Parser* parser){
	double damage;

	NextAlgo* next = new NextAlgo(sys, 0, writer, parser, 0.5, 1000);
	parser->startOver();
	sys->divide_all_lun_priorities(1);	
	next->runAlgo(3);
	writer->DeviceUtilStatistics("3");

	damage = sumDamage(next);
	delete next;

	return damage;
}

void SystemRecommendation::First_Optimal(SystemView* sys, Writer* writer, Parser* parser){
	OptimalAlgo* optimal = new OptimalAlgo(sys, 0, writer, parser);
	optimal->runAlgo(1);
	writer->DeviceUtilStatistics("1");
	writer->writeGroupRT("1");
	delete optimal;
}

void SystemRecommendation::First_Cost(SystemView* sys, Writer* writer, Parser* parser){
	CostAlgo* cost = new CostAlgo(sys, 0, writer, parser, 0.5, 1000, sys->getLimit());
	cost->runAlgo(1);
	writer->DeviceUtilStatistics("1");
	writer->writeGroupRT("1");
	delete cost;
}

double SystemRecommendation::RunCost(SystemView* sys, Writer* writer, Parser* parser){
	double damage;

	CostAlgo* cost = new CostAlgo(sys, 0, writer, parser, 0.5, 1000, sys->getLimit());
	parser->startOver();
	sys->divide_all_lun_priorities(1);	
	cost->runAlgo(3);
	writer->DeviceUtilStatistics("3");

	damage = sumDamage(cost);
	delete cost;

	return damage;	
}

double SystemRecommendation::RunOptimal(SystemView* sys, Writer* writer, Parser* parser){
	double damage;
	
	OptimalAlgo* optimal = new OptimalAlgo(sys, 0, writer,parser);
	parser->startOver();
	sys->divide_all_lun_priorities(1);	
	optimal->runAlgo(3);
	writer->DeviceUtilStatistics("3");

	damage = sumDamage(optimal);
	delete optimal;

	return damage;
}

/* input: max cost customer wants to spend on the system,
	 *		  total number of SATA devices required, 
	 *        algorithm to run: 1=Optimal 2=Next 3=Cost
	 *        dataFiles to run algorithm on
	 * output: [nof_SSD, nof_FC, nof_SATA]
	 */
int* SystemRecommendation::ChooseBest(int system_cost, int nof_SATA, Parser* parser, SystemView* sys, Writer* writer){
	// 10 is the number of time slices to run while looking for optimal system
	parser->setSystemRecommendation(1, 10);

	int actual_cost = 0;
	int nof_SSD = 0, nof_FC = 0;
	double damage, min_damage = 999999999;
	int* ans = new int[3];

	ans[2] = nof_SATA;
	actual_cost += nof_SATA * sys->getDevice("SATA")->getCost();
	if (actual_cost > system_cost){
		cout << "You cannot affort so much SATA. Increase system cost or decrease amount of SATA" << endl;
	}

	sys-> divide_system(nof_SSD, nof_FC, nof_SATA);
	switch(parser->whichAlgo){
	case 1:	First_Optimal(sys, writer, parser);
			break;
	case 2: First_Next(sys, writer, parser);
			break;
	case 3: First_Cost(sys, writer, parser);
			break;
	default:
		cout<<"NO ALGO TO RUN!!"<<endl;
	}

	// use SATA given, add one SSD and use leftover cost for FC 
	// until maximum SSD is reached
	while (actual_cost <= system_cost){		
		switch(parser->whichAlgo){
		case 1:	
			damage = RunOptimal(sys, writer, parser);
			break;
		case 2: 
			damage = RunNext(sys, writer, parser);
			break;
		case 3: 
			damage = RunCost(sys, writer, parser);
			break;
		default:
			cout<<"NO ALGO TO RUN!!"<<endl;
			break;
		}

		if (damage < min_damage){
			min_damage = damage;
			ans[0] = nof_SSD;
			ans[1] = nof_FC;
		}

		nof_SSD++; 
		actual_cost = nof_SATA * sys->getDevice("SATA")->getCost();
		actual_cost += nof_SSD * sys->getDevice("SSD")->getCost();
		nof_FC = (system_cost-actual_cost)/sys->getDevice("FC")->getCost(); 
		if (nof_FC >= 0) {
			sys->divide_system(nof_SSD, nof_FC, nof_SATA);
		}
	}

	sys->divide_all_lun_priorities(0);

	parser->setSystemRecommendation(0, -1);

	cout << "SR_SSD " << ans[0] << endl;
	cout << "SR_FC " << ans[1] << endl;
	cout << "SR_SATA " << ans[2] << endl;

	return ans;
}